/***************************************************************************************

***************************************************************************************/



/*
*************************************************************************************
* File name: 
* Function: 
*
*************************************************************************************
*/

#include "contributors.h"

#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/timeb.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "global.h"
#include "header.h"
#include "bitstream.h"
#include "vlc.h"
#include "image.h"
#include "rdopt_coding_state.h"
#include "AEC.h"    
#include "loopfilter.h"
#include "pixel.h"

void init_slice (int start_mb_addr,Picture *currPic);
static Slice *malloc_slice();

/*
*************************************************************************
* Function:This function terminates a slice
* Input:
* Output:
* Return: 0 if OK,                                                         \n
1 in case of error
* Attention:                                             
*************************************************************************
*/
int start_slice()
{
  Bitstream *currStream;

  currStream = currBitStream;

  if(img->current_mb_nr!=0)
    Demulate(currStream,current_slice_bytepos);               
#if M38817_DATA_STUFFING
  currStream->byte_buf <<= currStream->bits_to_go;
  if(currStream->bits_to_go == 1)
  {
	  currStream->byte_buf |= ( 0x3 >> 1 );
	  currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf;
	  currStream->streamBuffer[currStream->byte_pos++] = 0x80;
  }
  else{
	  currStream->byte_buf |= ( 0x3 << (currStream->bits_to_go - 2) );
	  currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf;
  }
  currStream->bits_to_go = 8;
  currStream->byte_buf = 0;
#else
  if (currStream->bits_to_go!=8) 
  {

    currStream->byte_buf <<= currStream->bits_to_go;
    currStream->byte_buf |= ( 1 << (currStream->bits_to_go - 1) );

    currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf;
    currStream->bits_to_go = 8;
    currStream->byte_buf = 0;
  } 
  else		
  {
    currStream->streamBuffer[currStream->byte_pos++] = 0x80;
    currStream->bits_to_go = 8;
    currStream->byte_buf = 0; 
  }
#endif
  current_slice_bytepos=currStream->byte_pos;  

  return 0;   
}

/*
*************************************************************************
* Function:This function terminates a picture
* Input:
* Output:
* Return: 0 if OK,                                                         \n
1 in case of error
* Attention:
*************************************************************************
*/
int terminate_picture()
{
  Bitstream *currStream;

  currStream = currBitStream;

  Demulate(currStream,current_slice_bytepos);              

  currStream->byte_buf <<= currStream->bits_to_go;
  currStream->byte_buf |= (1 << (currStream->bits_to_go - 1) ); 

  currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf;
  currStream->bits_to_go = 8;
  currStream->byte_buf = 0;

  return 0;   
}

/*
*************************************************************************
* Function:Encodes one slice
* Input:
* Output:
* Return: the number of coded MBs in the SLice 
* Attention:                                    
*************************************************************************
*/
void picture_data(Picture *pic)
{
	int	i;
	int CurrentMbNumber = 0;
	int MBRowSize = img->width / MB_BLOCK_SIZE;
	int slice_nr = 0;
	int slice_qp = img->qp;
	int len;
	int old_bit;
	int Bytes_After_Header;
	Boolean end_of_picture = FALSE;

	EncodingEnvironmentPtr eep;
	Slice *currSlice;
	Bitstream *currStream;
	
	int mb_width = img->width / 16;
	int slice_mb = input->slice_row_nr*mb_width;

	while (end_of_picture == FALSE) // loop over macroblocks
	{
		set_MB_parameters(CurrentMbNumber);

		//write slice header and do some initializations at the start of one slice
		if (input->slice_row_nr 
			&& (img->current_mb_nr == 0
				|| (img->current_mb_nr > 0 
				&& img->mb_data[img->current_mb_nr].slice_nr != img->mb_data[img->current_mb_nr - 1].slice_nr)))
		{
			old_bit = currBitStream->byte_pos * 8 + 8 - currBitStream->bits_to_go;
			start_slice();
			img->current_slice_start_mb = img->current_mb_nr;
			len = SliceHeader(slice_nr, slice_qp);

			init_slice(img->current_mb_nr, pic);
			Bytes_After_Header = img->currentSlice->partArr[0].bitstream->byte_pos;
			currSlice = img->currentSlice;

			for (i = 0; i < 1; i++)
			{
				(currSlice->partArr[i]).bitstream = currBitStream;
				currStream = (currSlice->partArr[i]).bitstream;
				currStream->write_flag = 0;
				eep = &((currSlice->partArr[i]).ee_AEC);

				if (currBitStream->bits_to_go < 8)
				{ // trailing bits to process
					currBitStream->byte_buf = (currBitStream->byte_buf << currBitStream->bits_to_go) | (0xff >> (8 - currBitStream->bits_to_go));
					stat->bit_use_stuffingBits[img->type] += currBitStream->bits_to_go;
					currBitStream->streamBuffer[currStream->byte_pos++] = currBitStream->byte_buf;
					currBitStream->bits_to_go = 8;
				}

				arienco_start_encoding(eep, currStream->streamBuffer, &(currStream->byte_pos), img->type);
			}

			AEC_new_slice();
			init_contexts();

			stat->bit_slice += len;
			slice_nr++;
			slice_header[img->type] += (currBitStream->byte_pos * 8 + 8 - currBitStream->bits_to_go - old_bit);
		}// slcie header end

		AEC_writting = 0;
		start_macroblock();

		encode_one_macroblock();

		if (input->p_sub_type_coding && img->type == P_IMG)
		{
			Macroblock  *currMB = &img->mb_data[img->current_mb_nr];
			int mv1[2];
			int mv2[2];
			int mv3[2];
			int mv4[2];
			int ref;

#ifndef P_SUB_OPT
			ref=(currMB->b8pdir[0*2+0]==-1?0:currMB->b8pdir[0*2+0]);
			mv1[0] = all_mincost[(img->pix_x>>2)+0][(img->pix_y>>2)+0][ref][4][1];
			mv1[1] = all_mincost[(img->pix_x>>2)+0][(img->pix_y>>2)+0][ref][4][2];
			img->p_sub_type_start += (abs(mv1[0]/4) + abs(mv1[1]/4)<4);

			ref=(currMB->b8pdir[0*2+1]==-1?0:currMB->b8pdir[0*2+1]);
			mv2[0] = all_mincost[(img->pix_x>>2)+2][(img->pix_y>>2)+0][ref][4][1];
			mv2[1] = all_mincost[(img->pix_x>>2)+2][(img->pix_y>>2)+0][ref][4][2];
			img->p_sub_type_start += (abs(mv2[0]/4) + abs(mv2[1]/4)<4);

			ref=(currMB->b8pdir[1*2+0]==-1?0:currMB->b8pdir[1*2+0]);
			mv3[0] = all_mincost[(img->pix_x>>2)+0][(img->pix_y>>2)+2][ref][4][1];
			mv3[1] = all_mincost[(img->pix_x>>2)+0][(img->pix_y>>2)+2][ref][4][2];
			img->p_sub_type_start += (abs(mv3[0]/4) + abs(mv3[1]/4)<4);

			ref=(currMB->b8pdir[1*2+1]==-1?0:currMB->b8pdir[1*2+1]);
			mv4[0] = all_mincost[(img->pix_x>>2)+2][(img->pix_y>>2)+2][ref][4][1];
			mv4[1] = all_mincost[(img->pix_x>>2)+2][(img->pix_y>>2)+2][ref][4][2];
			img->p_sub_type_start += (abs(mv3[0]/4) + abs(mv3[1]/4)<4);
#else
			ref = (currMB->b8pdir[0 * 2 + 0] == -1 ? 0 : currMB->b8pdir[0 * 2 + 0]);

			if ((img->type == P_IMG) && (ref == 3))
				ref = 0;

			mv1[0] = all_mincost[(img->pix_x >> 2) + 0][(img->pix_y >> 2) + 0][ref][4][1];
			mv1[1] = all_mincost[(img->pix_x >> 2) + 0][(img->pix_y >> 2) + 0][ref][4][2];
			img->p_sub_type_start += (abs(mv1[0]) + abs(mv1[1]) < 12);

			ref = (currMB->b8pdir[0 * 2 + 1] == -1 ? 0 : currMB->b8pdir[0 * 2 + 1]);

			if ((img->type == P_IMG) && (ref == 3))
				ref = 0;

			mv2[0] = all_mincost[(img->pix_x >> 2) + 1][(img->pix_y >> 2) + 0][ref][4][1];
			mv2[1] = all_mincost[(img->pix_x >> 2) + 1][(img->pix_y >> 2) + 0][ref][4][2];
			img->p_sub_type_start += (abs(mv2[0]) + abs(mv2[1]) < 12);

			ref = (currMB->b8pdir[1 * 2 + 0] == -1 ? 0 : currMB->b8pdir[1 * 2 + 0]);

			if ((img->type == P_IMG) && (ref == 3))
				ref = 0;

			mv3[0] = all_mincost[(img->pix_x >> 2) + 0][(img->pix_y >> 2) + 1][ref][4][1];
			mv3[1] = all_mincost[(img->pix_x >> 2) + 0][(img->pix_y >> 2) + 1][ref][4][2];
			img->p_sub_type_start += (abs(mv3[0]) + abs(mv3[1]) < 12);

			ref = (currMB->b8pdir[1 * 2 + 1] == -1 ? 0 : currMB->b8pdir[1 * 2 + 1]);

			if ((img->type == P_IMG) && (ref == 3))
				ref = 0;

			mv4[0] = all_mincost[(img->pix_x >> 2) + 1][(img->pix_y >> 2) + 1][ref][4][1];
			mv4[1] = all_mincost[(img->pix_x >> 2) + 1][(img->pix_y >> 2) + 1][ref][4][2];
			img->p_sub_type_start += (abs(mv4[0]) + abs(mv4[1]) < 12);
#endif
		}
		else if (img->type == I_IMG || img->type == B_IMG)
			img->p_sub_type_start = img->p_sub_type_start;
		else
			img->p_sub_type_start = 0;

		AEC_writting = 1;
		write_one_macroblock(1);

		//the last mb in current slice
		if (((img->coded_mb_nr + 1) % (slice_mb) == 0) || (img->coded_mb_nr + 1 == img->total_number_mb)){
			terminate_slice();
		}

		terminate_macroblock(&end_of_picture);
		proceed2nextMacroblock();
		CurrentMbNumber++;
	}

	old_bit = currBitStream->byte_pos * 8 + 8 - currBitStream->bits_to_go;
	terminate_picture();
	stat->bit_use_stuffingBits[img->type] += (currBitStream->byte_pos * 8 + 8 - currBitStream->bits_to_go - old_bit);

	DeblockFrame(img, imgY, imgU, imgV);

	// padding  
	image_padding(img, imgY, imgU, imgV, IMG_PAD_SIZE);

}

/*
*************************************************************************
* Function: allocates the memory for the coded picture data
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/
void AllocateBitstream()
{
	const int buffer_size = (img->width * img->height * 4);

	{
		if ((currBitStream = (Bitstream *)calloc(1, sizeof(Bitstream))) == NULL)
			no_mem_exit("malloc_slice: Bitstream");
		if ((currBitStream->streamBuffer = (byte *)calloc(buffer_size, sizeof(byte))) == NULL)
			no_mem_exit("malloc_slice: StreamBuffer");

		currBitStream->bits_to_go = 8;
	}
}
/*
*************************************************************************
* Function:free the allocated memory for the coded picture data
* Input:
* Output:
* Return: 
*************************************************************************
*/
void FreeBitstream()
{
  const int buffer_size = (img->width * img->height * 4); 

  if (currBitStream->streamBuffer)
    free(currBitStream->streamBuffer);
  if (currBitStream)
    free(currBitStream);
}


void Demulate(Bitstream *currStream, int current_slice_bytepos)
{
  int i, j;
  unsigned int rawbitsequence=-1;
  int bitvalue, nonzero, bitnum; 
  if(!(currBitStream->streamBuffer[current_slice_bytepos]==0 && currBitStream->streamBuffer[current_slice_bytepos+1]==0 && currBitStream->streamBuffer[current_slice_bytepos+2]==1))
    printf ("Fatal bitstream error");
  AllocatetmpBitstream();

  bitnum=8;
  tmpStream->bits_to_go=0;
  currStream->streamBuffer[currStream->byte_pos]=currStream->byte_buf<<currStream->bits_to_go;	

  for(tmpStream->byte_pos=i=current_slice_bytepos+3 ; i<=currStream->byte_pos; i++)
  { 
    for(j=8; j>(i==currStream->byte_pos? currStream->bits_to_go:0); j--)
    {
      bitvalue=currBitStream->streamBuffer[i]&(0x01<<(j-1));
      if(bitnum==2)
      {
        nonzero = rawbitsequence & 0x003fffff; // check successive 22 zeros.
        if(!nonzero)
        {
          tmpStream->streamBuffer[tmpStream->byte_pos] = 0x02; // insert '10' after 22 zeros
          tmpStream->byte_pos++;
          tmpStream->bits_to_go += 2;
          rawbitsequence = 0x00000002;
          bitnum = 8;
        }
      }

      rawbitsequence <<= 1;
      if(bitvalue)
      {
        tmpStream->streamBuffer[tmpStream->byte_pos] |= 1<<(bitnum-1);
        rawbitsequence |= 1;
      }
      else
      {
        tmpStream->streamBuffer[tmpStream->byte_pos] &= (~(1<<(bitnum-1)));
      }
      bitnum--;
      tmpStream->bits_to_go++;
      if(bitnum==0)
      {
        bitnum = 8;
        tmpStream->byte_pos++;
      }
    }

  }
  for(i=current_slice_bytepos+3; i<=tmpStream->byte_pos; i++)
  {
    currStream->streamBuffer[i]=tmpStream->streamBuffer[i];
  }

  currStream->byte_pos=tmpStream->byte_pos;	
  currStream->bits_to_go=8-tmpStream->bits_to_go%8;
  currStream->byte_buf=tmpStream->streamBuffer[tmpStream->byte_pos]>>currStream->bits_to_go;
  FreetmpBitstream();
}

void AllocatetmpBitstream()
{
  const int buffer_size = (img->width * img->height * 4); 

  if ((tmpStream = (Bitstream *) calloc(1, sizeof(Bitstream))) == NULL)
    no_mem_exit ("malloc_slice: Bitstream");
  if ((tmpStream->streamBuffer = (byte *) calloc(buffer_size, sizeof(byte))) == NULL)
    no_mem_exit ("malloc_slice: StreamBuffer");

  tmpStream->bits_to_go = 8;
}

void FreetmpBitstream()
{
  const int buffer_size = (img->width * img->height * 4); 

  if (tmpStream->streamBuffer)
    free(tmpStream->streamBuffer);
  if (tmpStream)
    free(tmpStream);
}
/*!
************************************************************************
* \brief
*    Initializes the parameters for a new slice and
*     allocates the memory for the coded slice in the Picture structure
*  \par Side effects:
*      Adds slice/partition header symbols to the symbol buffer
*      increments Picture->no_slices, allocates memory for the
*      slice, sets img->currSlice
*  \author
*    
************************************************************************
*/
void init_slice (int start_mb_addr,Picture *currPic)
{
  int i;
  Slice *curr_slice;
  DataPartition *dataPart;
  Bitstream *currStream;

  img->current_mb_nr = start_mb_addr;

  // Allocate new Slice in the current Picture, and set img->currentSlice
  assert (currPic != NULL);
  currPic->no_slices=1;
  if (currPic->no_slices >= MAXSLICEPERPICTURE)
    error ("Too many slices per picture, increase MAXLSICESPERPICTURE in global.h.", -1);
  currPic->slices[currPic->no_slices-1] = malloc_slice();
  curr_slice = currPic->slices[currPic->no_slices-1];
  img->currentSlice = curr_slice;

  curr_slice->picture_id = img->tr % 256;
  curr_slice->qp = img->qp;
  curr_slice->start_mb_nr = start_mb_addr;

  for (i = 0; i < 1; i++)
  {
    dataPart = &(curr_slice->partArr[i]);

      dataPart->writeSyntaxElement = writeSyntaxElement_AEC;

    currStream = dataPart->bitstream;
    currStream->bits_to_go = 8;
    currStream->byte_pos = 0;
    currStream->byte_buf = 0;
  }
}


/*!
************************************************************************
* \brief
*    Allocates a slice structure along with its dependentdata structures
* \return
*    Pointer to a Slice
*  \author
*    
************************************************************************
*/
static Slice *malloc_slice()
{
  int i;
  DataPartition *dataPart;
  Slice *slice;
  const int buffer_size = (img->width * img->height * 4);

  if ((slice = (Slice *) calloc(1, sizeof(Slice))) == NULL) no_mem_exit ("malloc_slice: slice structure");

  // create all context models
  slice->mot_ctx = create_contexts_MotionInfo();
  slice->tex_ctx = create_contexts_TextureInfo();
  slice->num_mb = 0;          // no coded MBs so far

  if ((slice->partArr = (DataPartition *) calloc(1, sizeof(DataPartition))) == NULL) no_mem_exit ("malloc_slice: partArr");
  for (i=0; i<1;i++) 
  {
    dataPart = &(slice->partArr[i]);
    if ((dataPart->bitstream = (Bitstream *) calloc(1, sizeof(Bitstream))) == NULL) no_mem_exit ("malloc_slice: Bitstream");
  }
  return slice;
}
/*!
************************************************************************
* \brief
*    This function terminates a slice (but doesn't write it out), 
*    the old terminate_slice (0)
* \return
*    0 if OK,                                                         \n
*    1 in case of error
*  \author
*      
*
************************************************************************
*/
int terminate_slice()
{
  Bitstream *currStream;
  Slice *currSlice = img->currentSlice;
  EncodingEnvironmentPtr eep;
  int i;

  write_terminating_bit (1);

  for (i=0; i<1; i++)
  {
    currStream = (currSlice->partArr[i]).bitstream;
    eep = &((currSlice->partArr[i]).ee_AEC);
    arienco_done_encoding(eep);
    currStream->bits_to_go = eep->Ebits_to_go;
    currStream->byte_buf   = eep->Ebuffer;    
  }          

  return 0;   
}


